using System;
using System.Collections.Generic;
using System.Linq;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;

namespace T.FAS.Runtime.DataAccess.Common
{
    /// <summary>
    /// CDO扩展方法
    /// </summary>
    public static class CDOCommonExtension
    {
        #region 初始化CDO
        /// <summary>
        /// 初始化CDO的引用
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static void InitCDO(this CompositeDataObject cdo)
        {
            if (cdo.CompositeNodeObjects != null && cdo.CompositeNodeObjects.Count > 0)
            {
                foreach (var node in cdo.CompositeNodeObjects)
                {
                    node.CompositeDataObject = cdo;
                }
            }
        }

        /// <summary>
        /// 构造领域对象之间的关联关系。
        /// 从Redis中拉取模型后需要以CompositeDataObject.DataObjects属性为锚点重新构建模型关系。
        /// 1、DM需要重新构建RootCompositeNodeObject属性
        /// 2、DMO需要重新构建CompositeDataObject、ParentObject、ChildCompositeNodeObjects、内联关系的Association属性（内联模型即为主模型）
        /// </summary>
        public static void BuildModelObjectRelationNew(this CompositeDataObject cdo)
        {
            try
            {
                //清空所有引用关系
                BreakModelObjectRelation(cdo);
                //构建DM.RootCompositeNodeObject属性
                cdo.RootCompositeNodeObject = cdo.CompositeNodeObjects.FirstOrDefault(i => i.ID == cdo.RootCompositeNodeObjectID);
                BuildModelObjectRelationNew(cdo, cdo.RootCompositeNodeObjectID);
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.CDO.InitError, CommonStringClassCommon.CDOCommonExtension_BuildModelObjectRelationNew, e);
            }
        }

        /// <summary>
        /// 构建新的模型关系，从redis中拉取模型后，关联关系引用无效需要重新构建。以CompositeNodeObjects为基础重新构造领域模型树形结构及引用关系
        /// </summary>
        /// <param name="CompositeDataObject">模型实例</param>
        /// <param name="currentNode">当前节点id</param>
        private static void BuildModelObjectRelationNew(CompositeDataObject CompositeDataObject, string currentNode)
        {
            var current = CompositeDataObject.CompositeNodeObjects.Where(node => string.Equals(node.ID, currentNode)).FirstOrDefault();
            if (current == null)
            {
                throw new FasException(DataModelErrorCode.CDO.InitError, string.Format(CommonStringClassCommon.CDOCommonExtension_BuildModelObjectRelationNew1,currentNode));
            }
            current.CompositeDataObject = CompositeDataObject; //构建DMO.CompositeDataObject属性关系

            foreach (var ass in current.Associations) //构建DMO.内联关系模型属性关系
            {
                if (ass.AssociateType == AssociateType.InnerJoin)
                {
                    ass.AssoDomaiModel = CompositeDataObject;
                    ass.AssoCompositeNodeObject = CompositeDataObject.CompositeNodeObjects.FirstOrDefault(i => i.ID == ass.AssoCompositeNodeObjectID);
                }
                else if (ass.AssociateType == AssociateType.OuterLeftJoin)
                {
                    //暂时不从Redis中获取外键关联DO，提高速度
                    //var _do = RTPMetadataHelper.GetRTPMetadata(ass.AssoDataObjectID) as DataObject;
                    //ass.AssoDataObject = _do;
                }
            }

            //对ChildCompositeNodeObjects进行统一排序，保证构造select查询时递归生成sql顺序的一致性
            var nodeList = CompositeDataObject.CompositeNodeObjects.Where(node => string.Equals(node.ParentObjectID, currentNode)).OrderBy(o => o.ID).ToList();
            if (nodeList != null && nodeList.Count > 0)
            {
                if (current.ChildCompositeNodeObjects == null)
                    current.ChildCompositeNodeObjects = new List<CompositeNodeObject>();
                else
                    current.ChildCompositeNodeObjects.Clear();   //舍弃原先的子节点关系
                current.ChildCompositeNodeObjects.AddRange(nodeList); //重新构建DMO.ChildCompositeNodeObjects属性关系
                foreach (var item in current.ChildCompositeNodeObjects)
                {
                    item.ParentObject = current; //构建DMO.ParentObject属性关系
                    BuildModelObjectRelationNew(CompositeDataObject, item.ID);
                }
            }
        }

        /// <summary>
        /// 打断领域对象之间的关联关系
        /// </summary>
        private static void BreakModelObjectRelation(CompositeDataObject cdo)
        {
            //置空需要重构引用关系的属性
            foreach (var modelObject in cdo.CompositeNodeObjects)
            {
                var childObjects = cdo.CompositeNodeObjects.Where(i => i.ParentObjectID == modelObject.ID);
                if (childObjects != null)
                {
                    foreach (var tempObject in childObjects)
                    {
                        tempObject.ParentObject = null;
                        tempObject.CompositeDataObject = null;
                        foreach (var ass in tempObject.Associations)
                        {
                            if (ass.AssociateType == AssociateType.InnerJoin)
                            {
                                ass.AssoDomaiModel = null;
                                ass.AssoCompositeNodeObject = null;
                            }
                        }
                    }
                }
            }
            cdo.RootCompositeNodeObject = null;
        }

        /// <summary>
        /// CDO是否包含状态机
        /// </summary>
        /// <param name="cdo"></param>
        /// <returns>true:包含状态机 false:不包含</returns>
        public static bool IsHaveStateMachine(this CompositeDataObject cdo)
        {
            if (cdo == null)
                throw new FasException(DataAccessErrorCode.NullValue, CommonStringClassCommon.CDOCommonExtension_IsHaveStateMachine);
            bool isHaveStatemachine = false;
            var eles = cdo.CompositeNodeObjects.FirstOrDefault(node => node.IsRootObject).Elements;
            if (eles != null && eles.Count > 0)
            {
                foreach (var ele in eles)
                {
                    if (ele.StateMachines != null && ele.StateMachines.Count > 0)
                    {
                        isHaveStatemachine = true;
                        break;
                    }
                }
            }
            return isHaveStatemachine;
        }

        /// <summary>
        /// CDO是否包含列权限控制
        /// </summary>
        /// <param name="cdo"></param>
        /// <returns></returns>
        public static bool IsHaveColumnPermission(this CompositeDataObject cdo)
        {
            bool isHaveColumnPermission = false;
            if (cdo == null)
                return isHaveColumnPermission;
            try
            {
                List<DataObject> dataObjects = new List<DataObject>();
                foreach (var node in cdo.CompositeNodeObjects)
                {
                    //是否需要处理外键关联DO
                    if (node.Associations?.Count > 0)
                    {
                        foreach (var asso in node.Associations)
                        {
                            if (asso.AssociateType == AssociateType.OuterLeftJoin)
                            {
                                if (asso.AssoDataObject != null && asso.AssoDataObject.IsHaveColumnPermission())
                                    dataObjects.Add(asso.AssoDataObject);
                            }
                        }
                    }
                    if (node.DataObject.IsHaveColumnPermission())
                        dataObjects.Add(node.DataObject);
                }
                isHaveColumnPermission = dataObjects.Count > 0;
            }
            catch (Exception e)
            {
            }
            return isHaveColumnPermission;
        }
        #endregion

        #region CNO.Element

        /// <summary>
        /// 是否为忽略前端输入的系统字段
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static bool IsIgnoreExternalInputSystemField(this CompositeNodeObjectElement element)
        {
            try
            {
                if (element.DataColumn != null && element.DataColumn.IsSystemField())
                {
                    if (!element.DataColumn.IsTimestampField())
                        return true;
                }
                return false;
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_IsIgnoreExternalInputSystemField,element.CompositeDataObjectID,element.CompositeNodeObjectID,element.ID), e);
            }
        }
        #endregion

        #region CNO
        /// <summary>
        /// 获取当前CNO中的指定列
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static DataColumn GetSpecifiedColumn(this CompositeNodeObject cno, string ColumnName)
        {
            try
            {
                var column = cno.DataObject.Columns.Where(col => col.ColumnName == ColumnName).FirstOrDefault();
                if (column == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetSpecifiedColumn,cno.CompositeDataObjectID,cno.ID,ColumnName));
                return column;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPkColumnFail,cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO的主键列
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static DataColumn GetPKColumn(this CompositeNodeObject cno)
        {
            try
            {
                var pkCol = cno.DataObject.Columns.Find(col => col.IsPkColumn);
                if (pkCol == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPKColumnInstanceFail,cno.CompositeDataObjectID,cno.ID));
                return pkCol;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPkColumnFail, cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO的主键列
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static DataColumn GetPKColumnForEngine(this CompositeNodeObject cno)
        {
            try
            {
                var pkCol = cno.DataObject.Columns.Find(col => col.IsPkColumn);
                if (pkCol == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPKColumnInstanceFail, cno.CompositeDataObjectID,cno.ID));
                return pkCol;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPkColumnFail, cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO节点关联父级CNO的元素Element
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static CompositeNodeObjectElement GetInnerAssociateElement(this CompositeNodeObject cno)
        {
            try
            {
                var inner = cno.Associations?.FirstOrDefault(asso => asso.AssociateType == AssociateType.InnerJoin);
                if (inner != null)
                {
                    var fkEle = cno.Elements.Find(ele => ele.ID == inner.Items.FirstOrDefault().SourceElementID);
                    if (fkEle == null)
                    {
                        throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetInnerAssociateElement,cno.CompositeDataObjectID,cno.ID));
                    }
                    return fkEle;
                }
                else
                    return null;
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetInnerAssociateElement1,cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO的主键元素Element
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static CompositeNodeObjectElement GetPKElement(this CompositeNodeObject cno)
        {
            try
            {
                var pkCol = cno.DataObject.Columns.Find(col => col.IsPkColumn);
                var pkEle = cno.Elements.Find(ele => ele.DataColumnID == pkCol.ID);
                if (pkEle == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPKElement,cno.CompositeDataObjectID,cno.ID));
                return pkEle;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetPkColumnFail, cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO节点的时间戳元素
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static CompositeNodeObjectElement GetTimestampElement(this CompositeNodeObject cno)
        {
            try
            {
                DataColumn timestampCol = cno.DataObject.Columns.FirstOrDefault(col => col.IsTimestampField());
                CompositeNodeObjectElement timestampEle = cno.Elements.Find(ele => ele.DataColumnID == timestampCol?.ID);
                if (timestampEle == null)
                    return null;
                //throw new Exception($"CDO:{cno.CompositeDataObjectID}的节点:{cno.ID}获取主键Element实例为空");
                return timestampEle;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetTimestampElement,cno.CompositeDataObjectID,cno.ID), e);
            }
        }

        /// <summary>
        /// 获取当前CNO的主键列
        /// </summary>
        /// <param name="cno"></param>
        /// <returns></returns>
        public static DataColumn GetTimestampColumnForEngine(this CompositeNodeObject cno)
        {
            try
            {
                DataColumn timestampCol = cno.DataObject.Columns.FirstOrDefault(col => col.IsTimestampField());
                return timestampCol;
            }
            catch (Exception e)
            {
                return null;
            }
        }

        /// <summary>
        /// 返回关联类型 类型：0: 1…N; 1: 1…1
        /// </summary>
        /// <param name="node"></param>
        /// <param name="parentNode"></param>
        /// <returns></returns>
        public static int GetRelationType(this CompositeNodeObject node, CompositeNodeObject parentNode)
        {
            try
            {
                if (node.DataObject == null || parentNode?.DataObject == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetRelationTypeFail,node.CompositeDataObjectID,node.ID));
                var parentDoId = parentNode.DataObject.ID;
                var relation = node.DataObject.DataObjectRelations?.FirstOrDefault(r => r.FromDoID == parentDoId);
                return relation.RelationType;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetRelationTypeFail, node.CompositeDataObjectID,node.ID), e);
            }
        }

        /// <summary>
        /// 返回关联类型 类型：0: 1…N; 1: 1…1
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static int GetRelationType(this CompositeNodeObject node)
        {
            try
            {
                var cdo = node.CompositeDataObject;
                if (cdo == null)
                    throw new Exception(string.Format(CommonStringClassCommon.CDOCommonExtension_CannotGetCNOParentCDOInstance,node.ID,node.CompositeDataObjectID));
                if (node.IsRootObject)
                    return 0;
                var parentNode = cdo.CompositeNodeObjects.FirstOrDefault(n => n.ID == node.ParentObjectID);
                if (node.DataObject == null || parentNode?.DataObject == null)
                    throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetRelationTypeFail, node.CompositeDataObjectID,node.ID));
                var parentDoId = parentNode.DataObject.ID;
                var relation = node.DataObject.DataObjectRelations?.FirstOrDefault(r => r.FromDoID == parentDoId);
                return relation.RelationType;
            }
            catch (Exception e)
            {

                throw new FasException(DataAccessErrorCode.Metadata.CDO.MetadataError, string.Format(CommonStringClassCommon.CDOCommonExtension_GetRelationTypeFail, node.CompositeDataObjectID,node.ID), e);
            }
        }

        /// <summary>
        /// 处理CNO节点的Alias别名，统一加入_List后缀
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static string GetCNODataObjectTableAlias(this CompositeNodeObject node)
        {
            var cdo = node.CompositeDataObject;
            if (cdo == null)
                throw new Exception(string.Format(CommonStringClassCommon.CDOCommonExtension_CannotGetCNOParentCDOInstance, node.ID,node.CompositeDataObjectID));
            if (node.IsRootObject)
                return node.DataObjectTableAlias;
            var pNode = cdo.CompositeNodeObjects.FirstOrDefault(n => n.ID == node.ParentObjectID);
            var relation = node.DataObject?.DataObjectRelations?.FirstOrDefault(r => r.FromDoID == pNode.DataObjectID);
            if (relation == null)
                throw new Exception(string.Format(CommonStringClassCommon.CDOCommonExtension_GetCNODataObjectTableAlias1,node.ID,node.DataObjectID,pNode.DataObjectID));
            return TUtil.GetPropertyName(node.DataObjectTableAlias, relation.RelationType == 0);
        }

        /// <summary>
        /// 处理CNO节点的Alias别名，统一加入_List后缀
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static List<CompositeNodeObject> GetAllChildNodes(this CompositeNodeObject node, CompositeDataObject cdo)
        {
            if (cdo == null)
                throw new Exception(string.Format(CommonStringClassCommon.CDOCommonExtension_CannotGetCNOParentCDOInstance, node.ID,node.CompositeDataObjectID));
            return GetAllChildNodes(cdo, node);
        }

        /// <summary>
        /// 处理CNO节点的Alias别名，统一加入_List后缀
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private static List<CompositeNodeObject> GetAllChildNodes(CompositeDataObject cdo, CompositeNodeObject node, CompositeNodeObject parNode = null)
        {
            if (cdo == null)
                throw new Exception(string.Format(CommonStringClassCommon.CDOCommonExtension_CannotGetCNOParentCDOInstance, node.ID,node.CompositeDataObjectID));
            List<CompositeNodeObject> nodes = new List<CompositeNodeObject>();
            var childs = cdo.CompositeNodeObjects.Where(n => n.ParentObjectID == node.ID);
            if (childs != null && childs.Count() > 0)
            {
                foreach (var child in childs)
                {
                    nodes.Add(child);
                    if (child.ChildCompositeNodeObjects != null && child.ChildCompositeNodeObjects.Count > 0)
                    {
                        nodes.AddRange(GetAllChildNodes(cdo, child, node));
                    }
                }
            }
            return nodes;
        }
        #endregion
    }
}
